import _thread
import threading
from tomorrow3 import threads
from queue import Queue
import random
from threadpool import ThreadPool, makeRequests
import unittest

# Linux不支持多线程，只支持重量级多进程， 所以Linux系统中最好运行多进程技术开发的程序。
# Windows系统支持多线程，所以可以在Windows系统中使用多线程技术开发的程序
#
# 并发：通过CPU调度算法，让用户看上去同时执行，实际上CPU操作层面不是真正的同时。
# 并行：多个CPU实例或多台机器同时执行一段处理逻辑，是真正的同时。

#多线程：用法可鼠标放在名称上双击并按住Ctrl，调转至源码，查看__init__函数，可将鼠标光标放在上面查看参数说明
import threadpool

_thread
#常用语法，function指对应的方法函数名，实际代码中可换为具体的；args为传入方法的参数，元组格式，args=();kwargs为字典格式。
# _thread.start_new_thread(function, args, kwargs=None)

threading
#常用语法，target指对应的方法函数名，实际代码中可换为具体的；args为传入方法的参数，元组格式，args=();kwargs为字典格式;daemon表示是否为守护线程，一般默认为非守护线程，为True时表示为守护线程。
# t1 = threading.Thread(target, args=(),kwargs=[],daemon)
# t1.start()

#存在守护线程时，当主线程运行完毕后，守护线程不管是否运行都会立刻结束，_thread为守护线程，所以一般要设置睡眠时间，等待守护线程运行完毕

#线程阻塞：阻塞主线程的运行，让主线程等待所以子线程运行结束后，再继续运行，一般应用在主线程为守护线程场景
#Thread().join()   #放在线程启动的后面位置


#线程锁：防止线程之间资源共享导致的线程安全问题，对访问的资源加上锁
# 1.获取锁对象
lock = threading.lock()
# 2.在方法里加锁，给资源运算和全局变量加锁
lock.acquire()
# 3.解锁
lock.release()

#信号量：控制多少个线程可以访问相同的资源

# 1.获取信号量对象
semapfore = threading.Semaphore(100)
# 2.获取信号量
semapfore.acquire()
# 3.释放信号量
semapfore.release()


#重写Threading的run函数，实现自定义Threading,继承Threading
    #创建继承threading.Thread类的类
class MyThread(threading.Thread):
    def __init__(self,func,args,name=None):
        threading.Thread.__init__(self)        #初始化父类init方法
        #super().__init__(self)                #初始化父类init方法
        self.func = func                       #把外界传的参数值传给MyThread类，并保存为类属性
        self.args = args
        self.name = name
    #重写run方法
    def run(self):
        return self.func(*self.args)           #调用外部传入的方法，*表达可接受多个参数
#定义要执行的任务方法
def add(x,y):
    result = x + y
    print(threading.current_thread().name + str(result))
#在main方法中启动多线程执行任务
def main():
    #定义两个线程
    t1 = MyThread(add,(1,2))
    t2 = MyThread(add,(3,4))
    #启动线程
    t1.start()
    t2.start()
#启动main方法
if __name__ == '__main__':
    main()


#队列：一种数据结构，队列的类型：双向队列、先进先出队列、后进先出队列、优先级队列等，队列的应用：亿级吞吐量消息队列Kafka

#创建队列
q = Queue(1000)

#队列常见操作方法：
# Queue.qsize()                               #返回队列的大小
# Queue.empty()                               #如果队列为空，返回True,反之False
# Queue.full()                                #如果队列满了，返回True,反之False
# Queue.full                                  #与 maxsize 大小对应
# Queue.get([block[, timeout]])               #获取队列，timeout等待时间
# Queue.get_nowait()                          #相当Queue.get(False)
# Queue.put(item)                             #写入队列，timeout等待时间
# Queue.put_nowait(item)                      #相当Queue.put(item, False)
# Queue.task_done()                           #在完成一项工作之后，Queue.task_done()函数向任务已经完成的队列发送一个信号
# Queue.join()                                #实际上意味着等到队列为空，再执行别的操作

#线程池：控制要启动的线程数量
threadpool
# 1.创建线程池
# pool = ThreadPool(5)
# 2.创建执行的任务
# requests = makeRequests(action,args)     #方法和要传递的参数值
# 3.将任务添加到线程池中，并执行
# [pool.putRequest(i) for i in requests]
# 4.等待执行完成
# pool.wait()

#tomorrow库:是基于concurrent.futures中的ThreadPoolExecutor实现的库，它实现了一个并发测试的装饰器，通过这个装饰器，我们可以简化代码，达到进行多线程并发测试的目的。

@threads(10)      # 代表最多启用10个线程运行，在方法前定义



#unittest框架：创建继承unittest.TestCase的类；unittest运行的测试用例，必须是继承了TestCase的方法，并且这个方法默认以test开头，默认执行顺序是在test以后，对比大小，按照ASCII码值

#TestCase:TestCase就是Unittest框架执行的测试用例。主要用法是，把测试用例编写在继承了TestCase的类中，通过unittest.main()来自动加载测试用例运行
#可以用unittest执行也可用普通方式执行

#跳过某个测试用例
@unittest.skip()

#TestFixture：TestFixture一般使用4个常见方法：setUp()、tearDown()、setUpClass()、tearDownClass()
class TestFixtureDemo(unittest.TestCase):
    def setUp(self):
        pass

    @classmethod          #类方法，可以不用实例化类，就可以访问，即TestFixtureDemo。setUpClass
    def setUpClass(self):
        pass

    def tearDown(self):
        pass

    @classmethod
    def tearDownClass(self):
        pass

    def testXX(self):
        pass


# - setUp()
#
#   每运行一个测试用例之前，先运行的函数；主要用于设置一些配置信息，静态属性等等
#
# - setUpClass()
#
#   它是类方法，必须和@classmethod装饰器结合使用（unittest设计如此）
#
#   实例化类后，会自动运行的方法，主要用于实例化类、设置某些环境配置如数据库连接配置等等。
#
# - tearDown()
#
#   每运行完一个测试用例之后，后运行的函数；主要用于销毁每个测试用例之间的数据，释放资源，还原数据
#
# - tearDownClass()
#
#   类中的代码全部运行完成后，会自动运行的方法，主要用于销毁类级别的资源，还原数据。

#unittest断言

# assertEqual(a,b)                     检查 a和b是否相等
# assertTrue(x)                        检查x是不是一个
# TrueassertIs(a,b)                    检查a和b是不是完全一样
# assertIsNone(x)                      检查x是不是一个
# NoneassertIn(a,b)                    检查a是不是b的子集
# assertIsInstance(a,b)                检查a、b两个对象，实例类型是否相同

class TestAssertDemo(unittest.TestCase):
    def setUp(self):
        self.l1, self.l2 = [1, 2], [1, 2]
        self.a, self.b = 1, 1

    def test01_assertEqual(self):
        self.assertEqual(self.a, self.b) # a和b相等

    def test02_assertIs(self):
        self.assertIs(self.l1, self.l2) # l1和l2不相同
        self.assertIs(self.a, self.b) # a和b相同

    def test03_assertTrue(self):
        self.assertTrue(self.a) # 1 是true
        self.assertTrue(0) # 0 是false

    def test04_assertIsNone(self):
        self.assertIsNone(self.b) # 1 不是 None
        self.assertIsNone(None)  # None 是 None

    def test05_assertIn(self):
        self.assertIn(self.l1, self.l2) # l1  不是l2的子集

    def test06_assertIsInstance(self):
        self.assertIsInstance(self.a, int) # a是int整型
        self.assertIsInstance(self.l1, list) # l1是list列表型


if __name__ == '__main__':
    unittest.main()


#TestSuite和TestRunner
    # 实例化测试套件
    # suite = unittest.TestSuite()

    # 添加测试用例到测试套件当中
    # suite.addTest(TestAssertDemo('test01'))   #括号里写上类名和方法名,添加单个测试用例
    # suite.addTests([TestAssertDemo('test01'),[TestAssertDemo('test02')])      #添加多个测试用例

    # 实例化test_runner
    # runner = unittest.TextTestRunner()

    # 使用runner运行测试套件
    # runner.run(suite)

#TestLoader加载测试用例

# TestLoader添加测试用例的方法有很多种，这里我们主要介绍两种
#
# 第一种：TestLoader().discover(("./", "*.py")
#
# 根据执行的路径来寻找指定的py文件中的测试用例，加载到测试套件当中。
#
# 第二种：TestLoader().loadTestsFromTestCase(测试用例类名)
#
# 根据测试用例的类名来加载测试用例

    #实例化testloaders
    # t1 = unittest.TestLoader()
    #使用testloader的对象t1来加载测试用例或测试套件
    # suite = t1.discover("./","work.py")      #搜索当前目录下的work.py文件
    # suite = t1.loadTestsFromTestCase(TestAssertDemo)        #加载对应类的测试用例
    #实例化runner并使用runner运行测试套件
    # runner = unittest.TextTestRunner()
    # runner.run(suite)


#TestResult
#TextTestRunner生成的报告

    #打开要保存的测试报告文件
    # with open("./result.txt",mode='w') as f:
    #实例化test_runner,verbosity表示报告的详细程度，0、1、2，2最详细
        # runner = unittest.TextTestRunner(f,verbosity=2,descriptions=True)
    # 实例化runner并使用runner运行测试套件
    #     runner.run(suite)

#HTMLTestRunner_PY3生成的报告

    # 打开要保存的测试报告文件
    # with open("./result.html",mode='wb') as f:
    # 实例化HTMLTestRunner_PY3_runner,verbosity表示报告的详细程度，0、1、2，2最详细
    # runner = HTMLTestRunner_PY3.HTMLTestRunner(f,verbosity=2,title)
    # 实例化runner并使用runner运行测试套件
    #     runner.run(suite)

# 多线程执行测试用例

def discover_case():
    #使用Testloader加载测试用例
    t1 = unittest.TestLoader()
    suite = t1.discover("./","test.py")
    return suite
def run_testsuite(suite):
    #实例化TextTestRunner
    runner = unittest.TextTestRunner()
    #使用runner运行测试套件
    result = runner.run(suite)
    return result
if __name__ == '__main__':
    #设置线程池
    pool = ThreadPool(10)
    #寻找测试套件
    suite = discover_case()
    #设置任务请求（执行测试用例run_testsuite)
    requests = threadpool.makeRequests(run_testsuite,suite)
    #把请求放入线程池中，运行起来
    for req in requests:
        pool.putRequest(req)
    pool.wait()